package work.linruchang.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.*;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 增强日期处理工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2021/10/14
 * @since 1.8
 **/
public class EnhanceDateUtil extends DateUtil {

    private static final String Date_Pattern_YYYY_DD = "yyyy-MM";

    /**
     * 将日期字符串转换为{@link DateTime}对象，格式：<br>
     * <ol>
     * <li>yyyy-MM-dd HH:mm:ss</li>
     * <li>yyyy/MM/dd HH:mm:ss</li>
     * <li>yyyy.MM.dd HH:mm:ss</li>
     * <li>yyyy年MM月dd日 HH时mm分ss秒</li>
     * <li>yyyy-MM-dd</li>
     * <li>yyyy/MM/dd</li>
     * <li>yyyy.MM.dd</li>
     * <li>HH:mm:ss</li>
     * <li>HH时mm分ss秒</li>
     * <li>yyyy-MM-dd HH:mm</li>
     * <li>yyyy-MM-dd HH:mm:ss.SSS</li>
     * <li>yyyyMMddHHmmss</li>
     * <li>yyyyMMddHHmmssSSS</li>
     * <li>yyyyMMdd</li>
     * <li>EEE, dd MMM yyyy HH:mm:ss z</li>
     * <li>EEE MMM dd HH:mm:ss zzz yyyy</li>
     * <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
     * <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
     * <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
     * <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li>
     * <p>
     * ===============================
     *
     * <li>yyyy-MM</li>  支持20211014
     * </ol>
     *
     * @param dateObj    日期字符串
     * @param thrownFlag 经过上述所有的格式解析，依然解析不出来，是否抛出异常  true抛出异常  false返回null
     * @return 日期
     */
    public static DateTime parseByObj(Object dateObj, boolean thrownFlag) {

        if (ObjectUtil.isNotNull(dateObj)) {
            try {
                if (dateObj instanceof Date) {
                    return DateUtil.date((Date) dateObj);
                } else {
                    return DateUtil.parse(dateObj.toString());
                }

            } catch (DateException e) {  //日期解析异常
                if (StrUtil.length(dateObj.toString()) == 7) {
                    return DateUtil.parse(dateObj.toString(), Date_Pattern_YYYY_DD);
                }
            }
            if (thrownFlag) {
                throw new DateException("No format fit for date String [{}] !", dateObj);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 日期字符串解析成日期对象
     *
     * @param dateStr 日期字符串
     * @return
     */
    public static DateTime parseByObj(Object dateStr) {
        return parseByObj(dateStr, false);
    }

    /**
     * 将特定格式的日期转换为Date对象
     *
     * @param dateStr 特定格式的日期
     * @param format  格式，空则使用{@link EnhanceDateUtil#parseByObj(Object)}进行解析日期
     * @return 日期对象
     */
    public static DateTime parseByObj(Object dateStr, String format) {
        return StrUtil.isBlank(format) ? parseByObj(dateStr) : DateUtil.parse(StrUtil.toString(dateStr), format);
    }


    /**
     * 获取【startDate,endDate】之间的日期，包括边界值
     *
     * @param startDate
     * @param endDate
     * @param dateResultPattern 返回结果集的日期格式，空则默认输出为yyyy-MM格式
     * @return
     */
    public static List<String> getDateRange(Date startDate, Date endDate, String dateResultPattern) {
        dateResultPattern = StrUtil.emptyToDefault(dateResultPattern, "yyyy-MM");
        List<DateTime> rangDateTimes = rangeToList(startDate, endDate, DateField.MONTH);
        List<String> result = Collections.emptyList();
        if (CollUtil.isNotEmpty(rangDateTimes)) {
            result = new ArrayList<>();
            for (DateTime dateTime : rangDateTimes) {
                result.add(DateUtil.format(dateTime, dateResultPattern));
            }
        }
        return result;
    }

    /**
     * 获取【startDate,endDate】之间的日期，包括边界值
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 结果集格式为 yyyy-MM
     */
    public static List<String> getDateRange(Date startDate, Date endDate) {
        return getDateRange(startDate, endDate, null);
    }

    /**
     * 获取【startDate,endDate】之间的日期，包括边界值
     *
     * @param startDateStr 开始日期
     * @param endDateStr   结束日期
     * @return 结果集格式为yyyy-MM
     */
    public static List<String> getDateRange(String startDateStr, String endDateStr) {
        return getDateRange(startDateStr, endDateStr, "", "");
    }

    /**
     * 获取【startDate,endDate】之间的日期，包括边界值
     *
     * @param startDateStr      开始日期
     * @param endDateStr        结束日期
     * @param dateResultPattern 返回结果集的日期格式，空则默认输出为yyyy-MM格式
     * @return 结果集 dateResultPattern空则格式为 yyyy-MM
     */
    public static List<String> getDateRange(String startDateStr, String endDateStr, String dateResultPattern) {
        DateTime startDate = parseByObj(startDateStr);
        DateTime endDate = parseByObj(endDateStr);
        return getDateRange(startDate, endDate, dateResultPattern);
    }

    /**
     * 获取【startDate,endDate】之间的日期，包括边界值
     *
     * @param startDateStr      开始日期
     * @param endDateStr        结束日期
     * @param dateParamPattern  startDate\endDate的日期格式
     * @param dateResultPattern 返回结果集的日期格式，空则默认输出为yyyy-MM格式
     * @return 结果集 dateResultPattern空则格式为 yyyy-MM
     */
    public static List<String> getDateRange(String startDateStr, String endDateStr, String dateParamPattern, String dateResultPattern) {
        DateTime startDate = parseByObj(startDateStr, dateParamPattern);
        DateTime endDate = parseByObj(endDateStr, dateParamPattern);
        return getDateRange(startDate, endDate, dateResultPattern);
    }


    /**
     * 日期转毫秒
     *
     * @param dateContent
     * @return
     */
    public static Long millTime(Object dateContent) {
        return Optional.ofNullable(parseByObj(dateContent))
                .map(Date::getTime)
                .orElse(null);
    }

    /**
     * 日期转秒
     *
     * @param dateContent
     * @return
     */
    public static Long secondTime(Object dateContent) {
        return Optional.ofNullable(millTime(dateContent))
                .map(millSecond -> millSecond / 1000)
                .orElse(null);
    }


    /**
     * 时间点（秒单位） 转成 日期对象
     *
     * @param secondTime
     * @return
     */
    public static Date secondTimeToDate(Object secondTime) {
        return Optional.ofNullable(secondTime)
                .filter(second -> NumberUtil.isNumber(second.toString()))
                .map(Convert::toLong)
                .map(second -> new DateTime(second * 1000))
                .orElse(null);
    }

    /**
     * 时间点（秒单位） 转成 日期字符串
     *
     * @param secondTime
     * @param datePatternStr
     * @return
     */
    public static String secondTimeToDateStr(Object secondTime, String datePatternStr) {
        return Optional.ofNullable(secondTimeToDate(secondTime))
                .map(date -> DateUtil.format(date, StrUtil.blankToDefault(datePatternStr, DatePattern.NORM_DATETIME_PATTERN)))
                .orElse(null);

    }

    /**
     * 时间点（秒单位） 转成 日期字符串
     *
     * @param secondTime
     * @return
     */
    public static String secondTimeToDateStr(Object secondTime) {
        return secondTimeToDateStr(secondTime, DatePattern.NORM_DATETIME_PATTERN);
    }


    /**
     * 日期范围合并
     *
     * @param sourceDateRanges
     * @return
     */
    public static List<Date[]> amalgamateDate(List<Date[]> sourceDateRanges) {
        sourceDateRanges = JSONUtil.toBean(JSONUtil.toJsonStr(CollUtil.emptyIfNull(sourceDateRanges)), new TypeReference<List<Date[]>>() {
        }, true);

        List<Date[]> result = new ArrayList<>();

        sourceDateRanges = sourceDateRanges.stream()
                .map(ArrayUtil::removeNull)
                .filter(sourceDateRange -> ArrayUtil.length(sourceDateRange) > 1)
                .peek(Arrays::sort)
                .map(sourceDateRange -> {
                    if (ArrayUtil.length(sourceDateRange) > 2) {
                        Date min = ArrayUtil.min(sourceDateRange);
                        Date max = ArrayUtil.max(sourceDateRange);
                        sourceDateRange = new Date[]{min, max};
                    }
                    return sourceDateRange;
                })
                .map(sourceDateRange -> Convert.convert(DateTime[].class, sourceDateRange))
                .sorted((sourceDateRange1, sourceDateRange2) -> CompareUtil.compare(sourceDateRange1[0], sourceDateRange2[0]))
                .collect(Collectors.toList());


        if (CollUtil.isNotEmpty(sourceDateRanges)) {
            Map<Integer, Integer> oldNewIndexMap = new HashMap<>();

            result.add(CollUtil.getFirst(sourceDateRanges));
            oldNewIndexMap.put(0, 0);

            for (int index = 1; index < sourceDateRanges.size(); index++) {
                Date[] preSourceDateRange = result.get(oldNewIndexMap.get(index - 1));
                Date[] currentIndexDateRange = sourceDateRanges.get(index);

                if (isIn(currentIndexDateRange[0], preSourceDateRange[0], preSourceDateRange[1])) {
                    preSourceDateRange[0] = CollUtil.min(Arrays.asList(preSourceDateRange[0], currentIndexDateRange[0]));
                    preSourceDateRange[1] = CollUtil.max(Arrays.asList(preSourceDateRange[1], currentIndexDateRange[1]));
                    oldNewIndexMap.put(index, oldNewIndexMap.get(index - 1));
                } else {
                    result.add(currentIndexDateRange);
                    oldNewIndexMap.put(index, CollUtil.size(result) - 1);
                }
            }

        }

        return result;
    }


    /**
     * 日期范围合并
     *
     * @param sourceDateStrRanges
     * @return
     */
    public static List<String[]> amalgamateDate(List<String[]> sourceDateStrRanges, final String resultDatePattern) {
        List<Date[]> sourceDateRanges = CollUtil.emptyIfNull(sourceDateStrRanges).stream()
                .map(ArrayUtil::removeBlank)
                .filter(sourceDateStrRange -> ArrayUtil.length(sourceDateStrRange) > 1)
                .map(sourceDateStrRange -> {
                    Date[] convertValue = new Date[sourceDateStrRange.length];
                    for (int i = 0; i < sourceDateStrRange.length; i++) {
                        convertValue[i] = parseByObj(sourceDateStrRange[i]);
                    }
                    return convertValue;
                })
                .map(ArrayUtil::removeNull)
                .collect(Collectors.toList());

        List<Date[]> amalgamateDateRangeResult = amalgamateDate(sourceDateRanges);

        return CollUtil.emptyIfNull(amalgamateDateRangeResult).stream()
                .map(amalgamateDateRange -> {
                    String[] convertValue = new String[amalgamateDateRange.length];
                    for (int i = 0; i < amalgamateDateRange.length; i++) {
                        convertValue[i] = format(amalgamateDateRange[i], StrUtil.blankToDefault(resultDatePattern, DatePattern.NORM_DATETIME_PATTERN));
                    }
                    return convertValue;
                })
                .collect(Collectors.toList());
    }

    /**
     * 13位或者10位时间戳转日期
     * @param dateTimeStamp 日期时间戳
     * @return
     */
    public static Date time13OR10ToDate(Number dateTimeStamp) {
        Long dateTimeStampLong = Convert.toLong(dateTimeStamp);
        String dateTimeStampLongStr = Convert.toStr(dateTimeStampLong);
        if(StrUtil.length(dateTimeStampLongStr) == 10 || StrUtil.length(dateTimeStampLongStr) == 13 ) {
            return DateUtil.date(StrUtil.length(dateTimeStampLongStr) == 10 ? dateTimeStampLong* 1000L : dateTimeStampLong);
        }
        return null;
    }


    /**
     * 入参dateObj是否不在beginDateObj、endDateObj日范围内
     * @param dateObj
     * @param beginDateObj
     * @param endDateObj
     * @return
     */
    public static boolean isNotIn(Object dateObj, Object beginDateObj, Object endDateObj) {
        return  !isIn(dateObj, beginDateObj,endDateObj);
    }

    /**
     * 入参dateObj是否在beginDateObj、endDateObj日范围内
     * @param dateObj
     * @param beginDateObj
     * @param endDateObj
     * @return
     */
    public static boolean isIn(Object dateObj, Object beginDateObj, Object endDateObj) {

        DateTime date = parseByObj(dateObj);
        DateTime beginDate = parseByObj(beginDateObj);
        DateTime endDate = parseByObj(endDateObj);


        if(ObjUtil.hasNull(date, beginDate, endDate)) {
            return false;
        }

        return isIn(date, beginDate, endDate);
    }

    public static void main(String[] args) {

        //DateTime startDate = EnhanceDateUtil.parse("2020-01");
        //System.out.println(startDate);
        //
        //startDate = DateUtil.parse("2020-01");
        //System.out.println(startDate);
        //
        //startDate  = DateUtil.parse("2020-01", "yyyy-MM");
        //System.out.println(startDate);
        //
        //List<String> dateRange = getDateRange(startDate, new Date());
        //System.out.println(dateRange);

        //DateTime parse = parse("2021-02", "");
        //System.out.println(parse);
        //
        //DateTime parse1 = DateUtil.parse("2021-02", "");
        //System.out.println(parse1);

        List<String> dateRange = getDateRange("2020-05", "2021-10");
        System.out.println(dateRange);
        dateRange = getDateRange("2021-05", "2021-05");
        System.out.println(dateRange);

    }

}
